#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>

// #define SelfDefine 1

namespace Protocol
{
    /***************************************
     * 请求: len \n "请求内容"\n  --> 请求内容格式: x op y
     *      // 1. 为了将请求单个分开，解决数据包粘报问题，确定报文间的边界，特意加上报头字段len用来表示请求内容的字符长度
     *      // 2. 为了将len字段和请求内容分开，我们添加'\n'字符，用来表示len字段的结束
     *      // 3. 第二个'\n'目的在于方便调试，无特殊意义
     *
     * 响应: len \n "响应结果"\n  ----> 响应内容格式: result code
     *
     ****************************************/

    const std::string ProtSep = " ";
    const std::string LineBreakSep = "\n";

    std::string Encode(const std::string &message)
    {
        std::string len = std::to_string(message.size());
        std::string package = len + LineBreakSep + message + LineBreakSep;
        return package;
    }

    bool Decode(std::string &package, std::string *message)
    {
        auto pos = package.find(LineBreakSep);
        if (pos == std::string::npos)
            return false;
        std::string lens = package.substr(0, pos);
        int messagelen = std::stoi(lens);

        int total = messagelen + lens.size() + 2 * LineBreakSep.size();
        if (package.size() < total)
            return false;

        *message = package.substr(pos + LineBreakSep.size(), messagelen);
        package.erase(0, total);
        return true;
    }

    class Request
    {
    public:
        Request()
            : _data_x(0), _data_y(0), _oper(0)
        {
        }
        Request(int x, int y, char op)
            : _data_x(x), _data_y(y), _oper(op)
        {
        }

        bool Serialize(std::string *out)
        {
#ifdef SelfDefine
            std::string content = std::to_string(_data_x) + ProtSep + _oper + ProtSep + std::to_string(_data_y);
            *out = content;
            return true;
#else
            Json::Value root;
            root["datax"] = _data_x;
            root["datay"] = _data_y;
            root["oper"] = _oper;

            Json::StyledWriter writer;
            *out = writer.write(root);
            return true;
#endif
        }

        bool Deserialize(std::string &in) // x op y
        {
#ifdef SelfDefine
            auto leftPos = in.find(ProtSep);
            if (leftPos == std::string::npos)
                return false;

            auto rightPos = in.rfind(ProtSep);
            if (rightPos == std::string::npos)
                return false;

            _data_x = std::stoi(in.substr(0, leftPos));
            _data_y = std::stoi(in.substr(rightPos + ProtSep.size()));
            std::string oper = in.substr(leftPos + ProtSep.size(), rightPos - (leftPos + ProtSep.size()));
            if (oper.size() != 1)
            {
                return false;
            }
            _oper = oper[0];
            return true;
#else
            Json::Value root;
            Json::Reader reader;
            bool res = reader.parse(in, root);
            if (res)
            {
                _data_x = root["datax"].asInt();
                _data_y = root["datay"].asInt();
                _oper = root["oper"].asInt();
            }
            return res;
#endif
        }

        int GetX() { return _data_x; }
        int GetY() { return _data_y; }
        char GetOp() { return _oper; }

    private:
        int _data_x;
        int _data_y;
        char _oper;
    };

    class Response
    {
    public:
        Response()
            : _result(0), _code(0)
        {
        }
        Response(int result, int code)
            : _result(result), _code(code)
        {
        }

        bool Serialize(std::string *out)
        {
#ifdef SelfDefine
            std::string content = std::to_string(_result) + ProtSep + std::to_string(_code);
            *out = content;
            return true;
#else
            Json::Value root;
            root["result"] = _result;
            root["code"] = _code;
            Json::FastWriter writer;
            *out = writer.write(root);
            return true;
#endif
        }

        bool Deserialize(std::string &in)
        {
#ifdef SelfDefine
            auto pos = in.find(ProtSep);
            if (pos == std::string::npos)
                return false;
            _result = std::stoi(in.substr(0, pos));
            _code = std::stoi(in.substr(pos + ProtSep.size()));
            return true;
#else
            Json::Value root;
            Json::Reader reader;
            bool res = reader.parse(in, root);
            if (res)
            {
                _result = root["result"].asInt();
                _code = root["code"].asInt();
            }
            return res;
#endif
        }

        int GetResult()
        {
            return _result;
        }

        int GetCode()
        {
            return _code;
        }

        void SetResult(int result)
        {
            _result = result;
        }

        void SetCode(int code)
        {
            _code = code;
        }

    private:
        int _result;
        int _code;
    };

    class Factory
    {
    public:
        std::shared_ptr<Request> BuildRequest()
        {
            std::shared_ptr<Request> req = std::make_shared<Request>();
            return req;
        }

        std::shared_ptr<Request> BuildRequest(int x, int y, char op)
        {
            std::shared_ptr<Request> req = std::make_shared<Request>(x, y, op);
            return req;
        }

        std::shared_ptr<Response> BuildResponse()
        {
            std::shared_ptr<Response> resp = std::make_shared<Response>();
            return resp;
        }

        std::shared_ptr<Response> BuildResponse(int result, int code)
        {
            std::shared_ptr<Response> resp = std::make_shared<Response>(result, code);
            return resp;
        }
    };
}